home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / TreeView.java < prev    next >
Text File  |  1998-10-20  |  65KB  |  2,285 lines

  1. package symantec.itools.awt;
  2.  
  3.  
  4. import java.awt.BorderLayout;
  5. import java.awt.Color;
  6. import java.awt.SystemColor;
  7. import java.awt.Dimension;
  8. import java.awt.Event;
  9. import java.awt.FontMetrics;
  10. import java.awt.Font;
  11. import java.awt.Graphics;
  12. import java.awt.Image;
  13. import java.awt.LayoutManager;
  14. import java.awt.Scrollbar;
  15. import java.awt.Panel;
  16. import java.awt.Rectangle;
  17. import java.awt.ItemSelectable;
  18. import java.awt.AWTEventMulticaster;
  19. import java.awt.event.ItemEvent;
  20. import java.awt.event.ItemListener;
  21. import java.awt.event.KeyEvent;
  22. import java.awt.event.KeyAdapter;
  23. import java.awt.event.MouseEvent;
  24. import java.awt.event.MouseAdapter;
  25. import java.awt.event.FocusEvent;
  26. import java.awt.event.FocusAdapter;
  27. import java.awt.event.AdjustmentEvent;
  28. import java.awt.event.AdjustmentListener;
  29. import java.awt.event.ActionEvent;
  30. import java.awt.event.ActionListener;
  31. import java.util.Vector;
  32. import java.beans.PropertyVetoException;
  33. import java.beans.PropertyChangeListener;
  34. import java.beans.VetoableChangeListener;
  35. import java.beans.PropertyChangeEvent;
  36. import java.util.ResourceBundle;
  37.  
  38.  
  39. //    01/15/97    RKM    Changed drawTree to make certain g1 has a font, before calling getFontMetrics on it
  40. //    01/15/07    RKM    Added invalidate to setTreeStructure
  41. //    01/29/97    TWB    Integrated changes from Windows and RKM's changes
  42. //     01/29/97    TWB    Integrated changes from Macintosh
  43. //  02/05/97    MSH Changed so that draws from first visible node
  44. //  02/27/97    MSH Merged change to add SEL_CHANGED
  45. //  04/02/97    TNM Draw all vertical lines
  46. //  04/14/97    RKM Changed bogus invalidates to repaint
  47. //                RKM    Changed hard coded sbVWidth to use preferredSize.width
  48. //                RKM Changed getTreeStructure so it actually returned a representation of what was in the treeview
  49. //                RKM    Rearranged a lot of stuff to get this to work
  50. //                RKM Changed g1.drawRect in drawTree to not overlap the scrollbar
  51. //                RKM Changed parseTreeStructure to not force a root node
  52. //  05/02/97    RKM Add arg to addSibling so caller could control whether the sible was added as the last sibling or not
  53. //                RKM    Changed insert to call addSibling with false when handling NEXT
  54. //                RKM    Kept addSibling with two params for compatibility
  55. //    05/31/97    RKM    Updated to support Java 1.1
  56. //                    Made properties bound & constrained
  57. //                    Removed get/setBackground & get/setForeground overrides, getter did nothing but call the super
  58. //                    and setters were calling repaint, no one else does this
  59. //                    Deprecated foreground and background hilite colors, used system colors instead
  60. //                    Deprecated SEL_CHANGED, replaced by ItemSelectable interface
  61. //                    Hid scrollbar on creation, to avoid ugly redraw problems
  62. //                    Changed to triggered redraw
  63. //                    NOTE: SystemColor seems to be broken on Mac
  64. //  06/01/97    RKM    Changed symantec.beans references to java.beans
  65. //  05/13/97    TNM Added horizontal scrollbar
  66. //  05/15/97    TNM Check for vendor to corect scrollbar problem
  67. //  06/09/97    CAR Modified check for vendor to include java.version 1.1.x
  68. //  06/19/97    TNM Merging changes
  69. //  07/25/97    CAR marked fields transient as needed will have to re-test after event handling code is changed
  70. //  08/14/97    RKM Changed hiliteColor to be consistent when on Mac
  71. //  08/20/97    LAB Changed protection of triggerRedraw to public from protected to allow the node
  72. //                    to trigger redraws.  Addresses Mac Bug #4372.  Changed privates to protecteds.
  73. //                    Reorganized the code in correspondence to the GoodBean Spec.  Deleted null
  74. //                    methods that had been deprecated.  Made some package level functions public.
  75. //                    Separated the internals of event hadling into protected handle<event> methods
  76. //                    to facilitate overriding.  Updated the InvalidTreeNodeException class to take
  77. //                    a message string for more detailed exceptions.  Fixed findLastPreSpace to handle
  78. //                    being passed null strings, or strings with no lenght (Addresses Mac Bug #4005).
  79. //                    Added clear method (Addresses Mac Bug #7369).  Fixed so selection is only set
  80. //                    if a single or double click occurs on a node, or if selected node was made
  81. //                    invisible by node collapse.  deprecated preferredSize and minimumSize in favor
  82. //                    of getPreferredSize and getMiniumumSize.  Made getPreferredSize calculate
  83. //                    the size TreeView should be based on the parts of the TreeView that are visible.
  84. //                    Added newTreeNode function to allow interception of the creation of nodes (Addresses
  85. //                    Win Bug #4174).  Took out isDesignTime code in paint that caused looping paints
  86. //                    (i.e. flickering) at design time.  Added paintTree(TreeNode, boolean) that
  87. //                    allows tree to be output with indenting (Win Bug #13095).  Fixed append to
  88. //                    make sure the node to append didn't already exist in the tree (Win Bug #13050).
  89. //                    Updated javadoc on append, insert, remove, removeSelected, etc. (Win Bug #12663).
  90. //  08/22/97    LAB    Call resetVector() before sending Action events so internal state is reset
  91. //                    (Addresses Win Bug #12666).  redraw now only calls resetVector if needed.
  92. //  08/26/97    CAR added null and zero length parameter checks to setTreeStructure
  93. //  08/28/97    CAR fixed bug re: horizontal scrolling not working
  94. //  08/28/97    RKM Added isFocusTraversable override (Yep, you can tab to this one)
  95. //  08/29/97    CAR modified getPreferredSize and getMinimumSize
  96. //  09/24/97    RKM Properly set isSun1_1 for Apple MRJ 2.0
  97. //                    Set isSun1_1 same as MutliList did (changed to static)
  98. //  10/04/97    LAB    Added ItemEvent firing when a node is expaned or collapsed.  Added
  99. //                    NODE_TOGGLED, NODE_EXPANDED, and NODE_COLLAPSED constants to support this.
  100. //  12/09/97    DS  Added check for a null itemListener in handleMousePressed
  101. //  12/19/97    DS  Added more checks for a null itemListener in handleMousePressed
  102. //  02/09/98    DS  Added support for hiding/showing nodes (TreeNode.setHidden(boolean)
  103. //                  Added support for deslecting a node (setSelectedNode(Tree Node)
  104.  
  105.  
  106. /**
  107.  * Creates an "outline view" of text headings and, optionally, images.
  108.  * The headings are arranged in a hierarchical fashion, and can be
  109.  * expanded to show their sub-headings or collapsed, hiding their
  110.  * sub-headings.
  111.  * <p>
  112.  * A TreeView is typically used to display information that is organized in a
  113.  * hierarchical fashion like an index or table of contents.
  114.  * <p>
  115.  * A TreeNode object is used for each heading.
  116.  * @see TreeNode
  117.  */
  118. public class TreeView extends Panel implements ItemSelectable
  119. {
  120.     // constants for insertion
  121.     /**
  122.      * Constant indicating that the new node is to be a child
  123.      * of the existing node.
  124.      * @see #insert
  125.      */
  126.     public static final int CHILD   = 0;
  127.     /**
  128.      * Constant indicating that the new node is to be the next
  129.      * sibling of the existing node.
  130.      * @see #insert
  131.      */
  132.     public static final int NEXT    = CHILD + 1;
  133.     /**
  134.      * Constant indicating that the new node is to be the last
  135.      * sibling of the existing node.
  136.      * @see #insert
  137.      */
  138.     public static final int LAST    = CHILD + 2;
  139.     /**
  140.      * Constand used to describe ItemEvents sent when a node is toggled.
  141.      * @see #NODE_EXPANDED
  142.      * @see #NODE_COLLAPSED
  143.      */
  144.     public static final int NODE_TOGGLED = 2001;
  145.     /**
  146.      * Constand used to detail that the node referenced in a NODE_TOGGLED ItemEvent has been expanded.
  147.      * @see #NODE_TOGGLED
  148.      * @see #NODE_COLLAPSED
  149.      */
  150.     public static final int NODE_EXPANDED = 2002;
  151.     /**
  152.      * Constand used to detail that the node referenced in a NODE_TOGGLED ItemEvent has been collapsed.
  153.      * @see #NODE_TOGGLED
  154.      * @see #NODE_EXPANDED
  155.      */
  156.     public static final int NODE_COLLAPSED = 2003;
  157.     /**
  158.      * @deprecated As of JDK version 1.1,
  159.      * replaced by ItemSelectable interface.
  160.      * @see java.awt.ItemSelectable
  161.      */
  162.     public static final int SEL_CHANGED = 1006; //selection changed event
  163.  
  164.     //
  165.     // Constructors
  166.     //
  167.  
  168.     /**
  169.      * Constructs an empty TreeView.
  170.      */
  171.     public TreeView()
  172.     {
  173.         super.setLayout(null);
  174.  
  175.         verticalScrollBar = new Scrollbar(Scrollbar.VERTICAL);
  176.         verticalScrollBar.hide();
  177.         add(verticalScrollBar);
  178.  
  179.         horizontalScrollBar = new Scrollbar(Scrollbar.HORIZONTAL);
  180.         horizontalScrollBar.hide();
  181.         add(horizontalScrollBar);
  182.  
  183.         needResetVector = true;
  184.     }
  185.  
  186.     /**
  187.      * Constructs a TreeView with the given node.
  188.      *
  189.      * @param head the root node of the constructed tree
  190.      */
  191.     public TreeView(TreeNode head)
  192.     {
  193.         this();
  194.         selectedNode = rootNode = head;
  195.         count = 1;
  196.     }
  197.  
  198.     //
  199.     // Properties
  200.     //
  201.  
  202.     /**
  203.      * Initializes the TreeView from a string array.
  204.      * There is one string for each node in the array. That string
  205.      * contains the text of the node indented with same number of
  206.      * leading spaces as the depth of that node in the tree.
  207.      * @param s the string array used for initialization.
  208.      * If null, the tree will be cleared.
  209.      * @see #getTreeStructure
  210.      */
  211.     public void setTreeStructure(String s[])
  212.     {
  213.         if (s == null || s.length == 0)
  214.         {
  215.             clear();
  216.             return;
  217.         }
  218.  
  219.         rootNode = selectedNode = null;
  220.         try
  221.         {
  222.             parseTreeStructure(s);
  223.         }
  224.         catch(InvalidTreeNodeException e)
  225.         {
  226.             System.err.println(e);
  227.         }
  228.  
  229.         triggerRedraw();
  230.  
  231.         invalidate();
  232.     }
  233.  
  234.     /**
  235.      * Gets a string array that reflects the current TreeView's contents.
  236.      * There is one string for each node in the array. That string
  237.      * contains the text of the node indented with same number of
  238.      * leading spaces as the depth of that node in the tree.
  239.      * @return the string array that reflects the TreeView's contents
  240.      * @see #setTreeStructure
  241.      */
  242.     public String[] getTreeStructure()
  243.     {
  244.         //Create a vector representing current tree structure
  245.         if (rootNode==null) return null;
  246.         Vector nodesVector = new Vector(count);
  247.         rootNode.depth = 0;
  248.         vectorize(rootNode, false, false, nodesVector);
  249.  
  250.         //Convert this to a String[]
  251.         int numNodes = nodesVector.size();
  252.         String[] treeStructure = new String[numNodes];
  253.         for (int i = 0;i < numNodes;i++)
  254.         {
  255.             TreeNode thisNode = (TreeNode)nodesVector.elementAt(i);
  256.  
  257.             //Add appropriate number of blanks
  258.             String treeString = "";
  259.             for (int numBlanks = 0;numBlanks < thisNode.depth;numBlanks++)
  260.                 treeString += ' ';
  261.  
  262.             //Add tree
  263.             treeString += thisNode.text;
  264.  
  265.             //Put string into array
  266.             treeStructure[i] = treeString;
  267.         }
  268.  
  269.         return treeStructure;
  270.     }
  271.  
  272.     //
  273.     // Deprecated Properties
  274.     //
  275.  
  276.     /**
  277.      * @deprecated As of JDK version 1.1,
  278.      * replaced by use of SystemColors.textHighlightText.
  279.      */
  280.     public Color getFgHilite()
  281.     {
  282.         return SystemColor.textHighlightText;
  283.     }
  284.  
  285.     /**
  286.      * @deprecated As of JDK version 1.1,
  287.      * replaced by use of SystemColors.textHighlight.
  288.      */
  289.     public Color getBgHilite()
  290.     {
  291.         return SystemColor.textHighlight;
  292.     }
  293.  
  294.     //
  295.     // ItemSelectable interface
  296.     //
  297.  
  298.     /**
  299.      * Returns the selected items or null if no items are selected.
  300.      * <p>
  301.      * This is a standard method of the ItemSelectable interface.
  302.      */
  303.     public Object[] getSelectedObjects()
  304.     {
  305.         if (selectedNode == null)
  306.             return null;
  307.  
  308.         TreeNode[] selectedObjects = new TreeNode[1];
  309.         selectedObjects[0] = selectedNode;
  310.  
  311.         return selectedObjects;
  312.     }
  313.  
  314.     //
  315.     // Methods
  316.     //
  317.  
  318.     // Insert a new node relative to a node in the tree.
  319.     // position = CHILD inserts the new node as a child of the node
  320.     // position = NEXT inserts the new node as the next sibling
  321.     // position = LAST inserts the new node as the last sibling
  322.     /**
  323.      * Inserts a new node relative to an existing node in the tree.
  324.      * @param newNode the new node to insert into the tree
  325.      * @param relativeNode the existing node used for a position reference
  326.      * @param position where to insert the new node relative to relativeNode.
  327.      * Legal values are CHILD, NEXT and LAST.
  328.      * @see #CHILD
  329.      * @see #NEXT
  330.      * @see #LAST
  331.      * @see #append
  332.     */
  333.     public void insert(TreeNode newNode, TreeNode relativeNode, int position)
  334.     {
  335.         if (newNode == null || relativeNode == null)
  336.             return;
  337.  
  338.         if (exists(relativeNode)==false)
  339.             return;
  340.  
  341.         switch (position)
  342.         {
  343.             case CHILD:
  344.                 addChild(newNode, relativeNode);
  345.                 break;
  346.  
  347.             case NEXT:
  348.                 addSibling(newNode, relativeNode, false);
  349.                 break;
  350.  
  351.             case LAST:
  352.                 addSibling(newNode, relativeNode, true);
  353.                 break;
  354.  
  355.             default:
  356.                 // invalid position
  357.                 return;
  358.         }
  359.     }
  360.  
  361.     /**
  362.      * Clears the tree structure and redraws.
  363.      */
  364.     public void clear()
  365.     {
  366.         rootNode = selectedNode = null;
  367.         count = 0;
  368.         v = new Vector();
  369.         e = new Vector();
  370.         triggerRedraw();
  371.  
  372.         invalidate();
  373.     }
  374.  
  375.     /**
  376.      * Returns the "root" node.
  377.      * The root node is the first top-level node in the tree hierarchy.
  378.      * All other nodes are either children or siblings of that one.
  379.      * @return the root tree node
  380.      */
  381.     public TreeNode getRootNode()
  382.     {
  383.         return rootNode;
  384.     }
  385.  
  386.     /**
  387.      * Returns the total number of nodes in the tree.
  388.      */
  389.     public int getCount()
  390.     {
  391.         return count;
  392.     }
  393.  
  394.     /**
  395.      * Returns the total number of viewable nodes in the tree.
  396.      * A node is viewable if all of its parents are expanded.
  397.      */
  398.     public int getViewCount()
  399.     {
  400.         return viewCount;
  401.     }
  402.  
  403.     /**
  404.      * Determines if the given node is viewable.
  405.      * A node is viewable if all of its parents are expanded.
  406.      * @param node the node to check
  407.      * @return true if the node is visible, false if it is not
  408.      * @see #viewable(java.lang.String)
  409.      */
  410.     boolean viewable(TreeNode node)
  411.     {
  412.         for (int i=0; i<viewCount; i++)
  413.         {
  414.             if (node == v.elementAt(i))
  415.             {
  416.                 return true;
  417.             }
  418.         }
  419.  
  420.         return false;
  421.     }
  422.  
  423.     /**
  424.      * Determines if the node with the given text is viewable.
  425.      * A node is viewable if all of its parents are expanded.
  426.      * @param s the node text to find
  427.      * @return true if the node is visible, false if it is not
  428.      * @see #viewable(TreeNode)
  429.      */
  430.     boolean viewable(String s)
  431.     {
  432.         if (s==null)
  433.         {
  434.             return false;
  435.         }
  436.  
  437.         for (int i=0; i<viewCount; i++)
  438.         {
  439.             TreeNode tn = (TreeNode)v.elementAt(i);
  440.  
  441.             if (tn.text != null)
  442.             {
  443.                 if (s.equals(tn.text))
  444.                 {
  445.                     return true;
  446.                 }
  447.             }
  448.         }
  449.  
  450.         return false;
  451.     }
  452.  
  453.     /**
  454.      * Determines if the given node is in the TreeView.
  455.      * @param node the node to check
  456.      * @return true if the node is in the TreeView, false if it is not
  457.      * @see #exists(java.lang.String)
  458.      */
  459.     public boolean exists(TreeNode node)
  460.     {
  461.         recount();
  462.  
  463.         for (int i=0; i<count; i++)
  464.         {
  465.             if (node == e.elementAt(i))
  466.             {
  467.                 return true;
  468.             }
  469.         }
  470.  
  471.         return false;
  472.     }
  473.  
  474.     /**
  475.      * Determines if the node with the given text is in the TreeView.
  476.      * @param s the node text to find
  477.      * @return true if the node is in the TreeView, false if it is not
  478.      * @see #exists(symantec.itools.awt.TreeNode)
  479.      */
  480.     public boolean exists(String s)
  481.     {
  482.         recount();
  483.  
  484.         if (s==null)
  485.         {
  486.             return false;
  487.         }
  488.  
  489.         for (int i=0; i<count; i++)
  490.         {
  491.             TreeNode tn = (TreeNode)e.elementAt(i);
  492.  
  493.             if (tn.text != null)
  494.             {
  495.                 if (s.equals(tn.text))
  496.                 {
  497.                     return true;
  498.                 }
  499.             }
  500.         }
  501.  
  502.         return false;
  503.     }
  504.  
  505.     /**
  506.      * Adds a new node at root level. If there is no root node, the given
  507.      * node is made the root node. If there is a root node, the given node
  508.      * is made a sibling of the root node.
  509.      * Does not redraw the component. This allows you to call repeatedly without
  510.      * causing the component to flicker while nodes are manipulated.  After all
  511.      * manipulation is done, repaint the tree.
  512.      * @param newNode the new node to add
  513.      * @see #insert
  514.      */
  515.     public void append(TreeNode newNode)
  516.     {
  517.         if (rootNode == null)
  518.         {
  519.             rootNode = newNode;
  520.             selectedNode = rootNode;
  521.             count = 1;
  522.             redrawTriggered = true;
  523.         }
  524.         else
  525.         {
  526.             recount();
  527.             if (e.contains(newNode))
  528.                 System.err.println(new InvalidTreeNodeException("append: " + errors.getString("NodeAlreadyExists")));
  529.             else
  530.                 addSibling(newNode, rootNode, true);
  531.         }
  532.     }
  533.  
  534.     /**
  535.      * Adds the specified child node to the specified parent node
  536.      * Does not redraw the component. This allows you to call repeatedly without
  537.      * causing the component to flicker while nodes are manipulated.  After all
  538.      * manipulation is done, repaint the tree.
  539.      * @param newNode the node to add as a child
  540.      * @param relativeNode the node to add the child to.
  541.      */
  542.     public void addChild(TreeNode newNode, TreeNode relativeNode)
  543.     {
  544.         if (relativeNode.child == null)
  545.         {
  546.             relativeNode.child = newNode;
  547.             newNode.parent = relativeNode;
  548.             count++;
  549.             redrawTriggered = true;
  550.         }
  551.         else
  552.         {
  553.             addSibling(newNode, relativeNode.child, true);
  554.         }
  555.  
  556.         relativeNode.numberOfChildren++;
  557.     }
  558.  
  559.     /**
  560.      * Adds the specified node as a sibling to the specified node
  561.      * Does not redraw the component. This allows you to call repeatedly without
  562.      * causing the component to flicker while nodes are manipulated.  After all
  563.      * manipulation is done, repaint the tree.
  564.      * @param newNode the node to add as a sibling
  565.      * @param siblingNode a sibling node to the new node.
  566.      * @see #addSibling(symantec.itools.awt.TreeNode, symantec.itools.awt.TreeNode, boolean)
  567.      */
  568.     public void addSibling(TreeNode newNode, TreeNode siblingNode)
  569.     {
  570.         addSibling(newNode,siblingNode,true);
  571.     }
  572.  
  573.     /**
  574.      * Adds the specified node as a sibling to the specified node
  575.      * Does not redraw the component. This allows you to call repeatedly without
  576.      * causing the component to flicker while nodes are manipulated.  After all
  577.      * manipulation is done, repaint the tree.
  578.      * @param newNode the node to add as a sibling
  579.      * @param siblingNode a sibling node to the new node.
  580.      * @param asLastSibling if true, then add the new node as the last (bottommost)
  581.      * sibling node to the specified sibling node.
  582.      * @see #addSibling(symantec.itools.awt.TreeNode, symantec.itools.awt.TreeNode)
  583.      */
  584.     public void addSibling(TreeNode newNode, TreeNode siblingNode, boolean asLastSibling)
  585.     {
  586.         if (asLastSibling)
  587.         {
  588.             //Find last sibling
  589.             TreeNode tempNode = siblingNode;
  590.             while (tempNode.sibling != null)
  591.                 tempNode = tempNode.sibling;
  592.  
  593.             tempNode.sibling = newNode;
  594.         }
  595.         else
  596.         {
  597.             //Insert the newNode below the siblingNode
  598.             newNode.sibling = siblingNode.sibling;
  599.  
  600.             siblingNode.sibling = newNode;
  601.         }
  602.  
  603.         //Set the parent of the new node to the parent of the sibling
  604.         newNode.parent = siblingNode.parent;
  605.  
  606.         count++;
  607.         redrawTriggered = true;
  608.     }
  609.  
  610.     /**
  611.      * Removes the node with the given text from the TreeView.
  612.      * Does not redraw the component. This allows you to call repeatedly without
  613.      * causing the component to flicker while nodes are manipulated.  After all
  614.      * manipulation is done, repaint the tree.
  615.      * @param s the node text to find
  616.      * @return the TreeNode removed from this TreeView or null if not found
  617.      * @see #remove(symantec.itools.awt.TreeNode)
  618.      * @see #removeSelected
  619.      */
  620.     public TreeNode remove(String s)
  621.     {
  622.         recount();
  623.  
  624.         for (int i=0; i<count; i++)
  625.         {
  626.             TreeNode tn = (TreeNode)e.elementAt(i);
  627.  
  628.             if (tn.text != null)
  629.             {
  630.                 if (s.equals(tn.text))
  631.                 {
  632.                     remove(tn);
  633.                     redrawTriggered = true;
  634.                     return tn;
  635.                 }
  636.             }
  637.         }
  638.  
  639.         return null;
  640.     }
  641.  
  642.     /**
  643.      * Removes the currently selected node from the TreeView.
  644.      * Does not redraw the component. This allows you to call repeatedly without
  645.      * causing the component to flicker while nodes are manipulated.  After all
  646.      * manipulation is done, repaint the tree.
  647.      * @see #remove(symantec.itools.awt.TreeNode)
  648.      * @see #remove(java.lang.String)
  649.      */
  650.     public void removeSelected()
  651.     {
  652.         if (selectedNode != null)
  653.         {
  654.             remove(selectedNode);
  655.         }
  656.     }
  657.  
  658.     /**
  659.      * Removes the given node from the TreeView.
  660.      * Does not redraw the component. This allows you to call repeatedly without
  661.      * causing the component to flicker while nodes are manipulated.  After all
  662.      * manipulation is done, repaint the tree.
  663.      * @param node the node to remove
  664.      * @return the TreeNode removed from this TreeView or null if not found
  665.      * @see #remove(java.lang.String)
  666.      * @see #removeSelected
  667.      */
  668.     public void remove(TreeNode node)
  669.     {
  670.         if (!exists(node))
  671.         {
  672.             return;
  673.         }
  674.  
  675.         if (node == selectedNode)
  676.         {
  677.             int index = v.indexOf(selectedNode);
  678.  
  679.             if (index == -1)
  680.             {    //not viewable
  681.                 index = e.indexOf(selectedNode);
  682.             }
  683.  
  684.             if (index > viewCount-1)
  685.             {
  686.                 index = viewCount-1;
  687.             }
  688.  
  689.             if (index>0)
  690.             {
  691.                 setSelectedNode((TreeNode)v.elementAt(index-1));
  692.             }
  693.             else if (viewCount>1)
  694.             {
  695.                 setSelectedNode((TreeNode)v.elementAt(1));
  696.             }
  697.         }
  698.  
  699.         // remove node and its decendents
  700.         if (node.parent != null)
  701.         {
  702.             if (node.parent.child == node)
  703.             {
  704.                 if (node.sibling != null)
  705.                 {
  706.                     node.parent.child = node.sibling;
  707.                 }
  708.                 else
  709.                 {
  710.                     node.parent.child = null;
  711.                     node.parent.collapse();
  712.                 }
  713.             }
  714.             else
  715.             {
  716.                 TreeNode tn=node.parent.child;
  717.  
  718.                 while (tn.sibling != node)
  719.                 {
  720.                     tn = tn.sibling;
  721.                 }
  722.  
  723.                 if (node.sibling != null)
  724.                 {
  725.                     tn.sibling = node.sibling;
  726.                 }
  727.                 else
  728.                 {
  729.                     tn.sibling = null;
  730.                 }
  731.             }
  732.         }
  733.         else
  734.         {
  735.             if (node == rootNode)
  736.             {
  737.                 if (node.sibling == null)
  738.                 {
  739.                     rootNode=null;
  740.                 }
  741.                 else
  742.                 {
  743.                     rootNode=node.sibling;
  744.                 }
  745.             }
  746.             else
  747.             {
  748.                 TreeNode tn = rootNode;
  749.  
  750.                 while (tn.sibling != node)
  751.                 {
  752.                     tn = tn.sibling;
  753.                 }
  754.  
  755.                 if (node.sibling != null)
  756.                 {
  757.                     tn.sibling = node.sibling;
  758.                 }
  759.                 else
  760.                 {
  761.                     tn.sibling = null;
  762.                 }
  763.             }
  764.         }
  765.  
  766.         recount();
  767.         redrawTriggered = true;
  768.     }
  769.  
  770.     /**
  771.      * Print out the text of each node in the TreeView beginning with
  772.      * the given node.
  773.      * The nodes are printed out one per line with no indenting.
  774.      * @param node the first node to print
  775.      */
  776.     public void printTree(TreeNode node)
  777.     {
  778.         printTree(node, false);
  779.     }
  780.  
  781.     /**
  782.      * Print out the text of each node in the TreeView beginning with
  783.      * the given node.
  784.      * @param node the first node to print
  785.      * @param isIndented, if true, nodes will have hierarchical indenting,
  786.      * if false, all nodes will pe printed at the same level
  787.      */
  788.     public void printTree(TreeNode node, boolean isIndented)
  789.     {
  790.         if (node == null)
  791.         {
  792.             return;
  793.         }
  794.         String padding = new String();
  795.         if (isIndented)
  796.         {
  797.             for (int i = 0; i < node.depth; i++)
  798.                 padding = "  " + padding;
  799.         }
  800.  
  801.         System.out.println(padding + node.text);
  802.         printTree(node.child, isIndented);
  803.         printTree(node.sibling, isIndented);
  804.     }
  805.  
  806.     /**
  807.      * Gets the currently selected node.
  808.      * @return the currently selected node, or null if none selected
  809.      */
  810.     public TreeNode getSelectedNode()
  811.     {
  812.         return selectedNode;
  813.     }
  814.  
  815.     /**
  816.      * Gets the text of the currently selected node.
  817.      * @return the text of the currently selected node or null if no node
  818.      * is selected
  819.      */
  820.     public String getSelectedText()
  821.     {
  822.         if (selectedNode == null)
  823.             return null;
  824.  
  825.         return selectedNode.getText();
  826.     }
  827.  
  828.     // -----------------------------------------
  829.     // --------- graphics related methods ------
  830.     // -----------------------------------------
  831.     /**
  832.      * Handles redrawing of this component on the screen.
  833.      * This is a standard Java AWT method which gets called by the Java
  834.      * AWT (repaint()) to handle repainting this component on the screen.
  835.      * The graphics context clipping region is set to the bounding rectangle
  836.      * of this component and its [0,0] coordinate is this component's
  837.      * top-left corner.
  838.      * Typically this method paints the background color to clear the
  839.      * component's drawing space, sets graphics context to be the foreground
  840.      * color, and then calls paint() to draw the component.
  841.      *
  842.      * It is overridden here to reduce flicker by eliminating the uneeded
  843.      * clearing of the background.
  844.      *
  845.      * @param g the graphics context
  846.      * @see java.awt.Component#repaint
  847.      * @see #paint
  848.      */
  849.     public void update (Graphics g)
  850.     {
  851.         //(eliminates background draw to reduce flicker)
  852.         paint(g);
  853.     }
  854.  
  855.     /**
  856.      * Paints this component using the given graphics context.
  857.      * This is a standard Java AWT method which typically gets called
  858.      * by the AWT to handle painting this component. It paints this component
  859.      * using the given graphics context. The graphics context clipping region
  860.      * is set to the bounding rectangle of this component and its [0,0]
  861.      * coordinate is this component's top-left corner.
  862.      *
  863.      * @param g the graphics context used for painting
  864.      * @see java.awt.Component#repaint
  865.      * @see #update
  866.      */
  867.     public void paint (Graphics g)
  868.     {
  869.         Dimension d = size();
  870.  
  871.         if (redrawTriggered || (d.width != viewWidth) || (d.height != viewHeight))
  872.         {
  873.             // redraw needed, or size has changed
  874.             redraw(g);
  875.         }
  876.  
  877.         g.translate(-sbHPosition, 0);
  878.         g.clearRect(sbHPosition,0,d.width-sbVWidth,d.height-sbHHeight);
  879.         if (sbVShow && sbHShow)
  880.         {
  881.             g.setColor(Color.lightGray);
  882.             g.fillRect(sbHPosition+d.width-sbVWidth, d.height-sbHHeight, sbVWidth, sbHHeight);
  883.         }
  884.         g.clipRect(sbHPosition,0,d.width-sbVWidth,d.height-sbHHeight);
  885.         g.drawImage(im1, 0, 0, this);
  886.         g.setColor(Color.black);
  887.         g.drawRect(sbHPosition,0, d.width-sbVWidth-1, d.height-sbHHeight-1);
  888.  
  889.     }
  890.  
  891.     /**
  892.      * Lays out the vertical scrollbar as needed, then draws the TreeView into
  893.      * an offscreen image. This is used for cleaner refresh.
  894.      */
  895.     public void redraw()
  896.     {
  897.         //For backward compatibality. Do not allow to call only redraw() without recalculation.
  898.         triggerRedraw();
  899.     }
  900.  
  901.     /**
  902.      * Repaints this component.
  903.      * @param f <code>true</code>to re-vectorize if the node arrangement has changed
  904.      */
  905.     public void repaint(boolean f)
  906.     {
  907.         if(f)
  908.         {
  909.             needResetVector = true;
  910.         }
  911.  
  912.         triggerRedraw();
  913.     }
  914.  
  915.     /**
  916.      * Lays out the vertical scrollbar as needed, then draws the TreeView into
  917.      * an offscreen image. This is used for cleaner refresh.
  918.      * @param g the graphics object use for drawing
  919.      */
  920.     public void redraw(Graphics g)
  921.     {
  922.         Dimension d = size();
  923.  
  924.         redrawTriggered = false;
  925.  
  926.         if(needResetVector)
  927.             resetVector();
  928.         else
  929.             needResetVector = true;
  930.  
  931.         newWidth = compWidth(g);
  932.  
  933.         int inRectCount = ((d.height - sbHHeight) / cellSize);
  934.  
  935.         if (viewCount > inRectCount)
  936.         {
  937.               // need the vertical scrollbar
  938.             sbVShow  = true;
  939.             sbVWidth = verticalScrollBar.preferredSize().width;
  940.         }
  941.         else
  942.         {
  943.               sbVShow     = false;
  944.             sbVWidth    = 0;
  945.             sbVPosition = 0;
  946.         }
  947.  
  948.         if (newWidth > (d.width - sbVWidth))
  949.         {
  950.             // need the horizontal scrollbar
  951.             sbHShow = true;
  952.             sbHHeight = horizontalScrollBar.preferredSize().height;
  953.         }
  954.         else
  955.         {
  956.             sbHShow     = false;
  957.             sbHHeight   = 0;
  958.             sbHPosition = 0;
  959.         }
  960.  
  961.         drawTree();
  962.  
  963.         if (sbVShow)
  964.         {
  965.             verticalScrollBar.reshape(d.width-sbVWidth,0,sbVWidth,d.height-sbHHeight);
  966.             verticalScrollBar.setValues(sbVPosition, inRectCount, 0, viewCount-(isSun1_1?0:inRectCount));
  967.             verticalScrollBar.setPageIncrement(inRectCount-1);
  968.             verticalScrollBar.show();
  969.         }
  970.         else
  971.         {
  972.             verticalScrollBar.hide();
  973.         }
  974.  
  975.         if (sbHShow)
  976.         {
  977.             horizontalScrollBar.reshape(0,d.height-sbHHeight,d.width-sbVWidth,sbHHeight);
  978.             horizontalScrollBar.setValues(sbHPosition, d.width-sbVWidth, 0, sbHSize-(isSun1_1?0:(d.width-sbVWidth)));
  979.             horizontalScrollBar.setPageIncrement(d.width-sbVWidth);
  980.             horizontalScrollBar.setLineIncrement(sbHLineIncrement);
  981.             horizontalScrollBar.show();
  982.         }
  983.         else
  984.         {
  985.             horizontalScrollBar.hide();
  986.         }
  987.     }
  988.  
  989.     /**
  990.      * Draws the TreeView into an offscreen image. This is used for cleaner refresh.
  991.      */
  992.     public void drawTree()
  993.     {
  994.         Dimension d = size();
  995.  
  996.         if(needResetVector)
  997.             resetVector();
  998.  
  999.         if ((d.width != viewWidth) || (d.height != viewHeight) || (g1 == null) || (sbHSize != newWidth))
  1000.         {
  1001.             // size has changed, must resize image
  1002.             im1 = createImage(Math.max(sbHSize=newWidth, d.width), d.height);
  1003.  
  1004.             if (g1 != null)
  1005.             {
  1006.                 g1.dispose();
  1007.             }
  1008.  
  1009.             g1         = im1.getGraphics();
  1010.             if ( g1.getClip() == null )
  1011.                 g1.clipRect( 0, 0, Math.max(sbHSize=newWidth, d.width), d.height );
  1012.             viewWidth  = d.width;
  1013.             viewHeight = d.height;
  1014.         }
  1015.  
  1016.         Font f = getFont();  //unix version might not provide a default font
  1017.  
  1018.         //Make certain there is a font
  1019.         if (f == null)
  1020.         {
  1021.             f = new Font("Serif", Font.PLAIN, 13);
  1022.             g1.setFont(f);
  1023.             setFont(f);
  1024.         }
  1025.  
  1026.         //Make certain the graphics object has a font (Mac doesn't seem to)
  1027.         if (g1.getFont() == null)
  1028.             g1.setFont(f);
  1029.  
  1030.         fm = g1.getFontMetrics();
  1031.         g1.setColor(getBackground());
  1032.         g1.fillRect(0, 0, im1.getWidth(this), d.height);// clear image
  1033.  
  1034.         //do drawing for each visible node
  1035.         int lastOne = sbVPosition + viewHeight / cellSize + 1;
  1036.  
  1037.         if (lastOne > viewCount)
  1038.         {
  1039.             lastOne = viewCount;
  1040.         }
  1041.  
  1042.         TreeNode outerNode = null;
  1043.  
  1044.         if (!v.isEmpty())
  1045.             outerNode = (TreeNode)v.elementAt(sbVPosition);
  1046.  
  1047.         for (int i = sbVPosition; i < lastOne; i++)
  1048.         {
  1049.             TreeNode node = (TreeNode)v.elementAt(i);
  1050.             int x         = cellSize*(node.depth + 1);
  1051.             int y         = (i - sbVPosition) * cellSize;
  1052.  
  1053.             // draw lines
  1054.             g1.setColor(getForeground());
  1055.  
  1056.             // draw vertical sibling line
  1057.             if (node.sibling != null && node.isASiblingVisible())
  1058.             {
  1059.                 int k = v.indexOf(node.sibling) - i;
  1060.  
  1061.                 if (k > lastOne)
  1062.                 {
  1063.                     k = lastOne;
  1064.                 }
  1065.  
  1066.                 drawDotLine(x - cellSize/2, y + cellSize/2,
  1067.                             x - cellSize/2, y + cellSize/2 +  k*cellSize);
  1068.  
  1069.             }
  1070.  
  1071.             // if sibling is above page, draw up to top of page for this level
  1072.             for (int m = 0; m < i; m++)
  1073.             {
  1074.                 TreeNode sib = (TreeNode)v.elementAt(m);
  1075.  
  1076.                 if ((sib.sibling == node) && (m < sbVPosition))
  1077.                 {
  1078.                     drawDotLine (x - cellSize / 2, 0,
  1079.                                  x - cellSize / 2, y + cellSize / 2);
  1080.                 }
  1081.             }
  1082.  
  1083.             // draw vertical child lines
  1084.             if (node.isExpanded() && node.isAChildVisible())
  1085.             {
  1086.                 drawDotLine(x + cellSize / 2, y + cellSize - 2 ,
  1087.                             x + cellSize / 2, y + cellSize + cellSize / 2);
  1088.             }
  1089.  
  1090.             // draw node horizontal line
  1091.             g1.setColor(getForeground());
  1092.             drawDotLine(x - cellSize / 2, y + cellSize / 2,
  1093.                         x + cellSize / 2, y + cellSize / 2);
  1094.  
  1095.             // draw toggle box
  1096.             drawNodeToggle(node, x, y);
  1097.  
  1098.             // draw node image
  1099.             Image nodeImage = node.getImage();
  1100.  
  1101.             if (nodeImage != null)
  1102.             {
  1103.                 g1.drawImage(nodeImage, x + imageInset, y, this);
  1104.             }
  1105.  
  1106.             // draw node text
  1107.             if (node.text != null)
  1108.             {
  1109.                 drawNodeText(node, y, node == selectedNode, false);
  1110.             }
  1111.  
  1112.             if(outerNode.depth > node.depth)
  1113.                 outerNode = node;
  1114.         }
  1115.  
  1116.         // draw outer vertical lines
  1117.         if (outerNode != null)
  1118.         {
  1119.             while((outerNode = outerNode.parent) != null)
  1120.             {
  1121.                 if (outerNode.sibling != null && outerNode.isASiblingVisible())
  1122.                     drawDotLine (cellSize * (outerNode.depth + 1) - cellSize / 2, 0,
  1123.                                  cellSize * (outerNode.depth + 1) - cellSize / 2, d.height);
  1124.             }
  1125.         }
  1126.  
  1127.         needResetVector = true;
  1128.     }
  1129.  
  1130.     /**
  1131.      * Used to draw the toggle box of an expandable node.
  1132.      * Override to change the look of the toggle box.
  1133.      */
  1134.     protected void drawNodeToggle(TreeNode node, int x, int y)
  1135.     {
  1136.         if(node.isExpandable() && node.isAChildVisible())
  1137.         {
  1138.             g1.setColor(getBackground());
  1139.             g1.fillRect(cellSize * (node.depth) + cellSize / 4, y + clickSize / 2, clickSize, clickSize);
  1140.             g1.setColor(getForeground());
  1141.             g1.drawRect(cellSize * (node.depth) + cellSize / 4, y + clickSize / 2, clickSize, clickSize);
  1142.  
  1143.             // cross hair
  1144.             g1.drawLine(cellSize * (node.depth) + cellSize / 4 + 2,             y + cellSize / 2,
  1145.                         cellSize * (node.depth) + cellSize / 4 + clickSize - 2, y + cellSize / 2);
  1146.  
  1147.             if(!(node.isExpanded()))
  1148.             {
  1149.                 g1.drawLine(cellSize * (node.depth) + cellSize / 2, y + clickSize / 2 + 2,
  1150.                             cellSize * (node.depth) + cellSize / 2, y + clickSize / 2 + clickSize - 2);
  1151.             }
  1152.         }
  1153.     }
  1154.  
  1155.     /**
  1156.      * Returns the recommended dimensions to properly display this component.
  1157.      * This is a standard Java AWT method which gets called to determine
  1158.      * the recommended size of this component.
  1159.      *
  1160.      * @see #getMinimumSize
  1161.      */
  1162.     public synchronized Dimension getPreferredSize()
  1163.     {
  1164.         Dimension p = size();
  1165.         Dimension m = getMinimumSize();
  1166.         return new Dimension(Math.max(p.width, m.width), Math.max(p.height, m.height));
  1167.     }
  1168.  
  1169.     /**
  1170.      * @deprecated
  1171.      * @see #getPreferredSize
  1172.      */
  1173.     public synchronized Dimension preferredSize()
  1174.     {
  1175.         return getPreferredSize();
  1176.     }
  1177.  
  1178.     /**
  1179.      * Returns the minimum dimensions to properly display this component.
  1180.      * This is a standard Java AWT method which gets called to determine
  1181.      * the minimum size of this component.
  1182.      *
  1183.      * @see #getPreferredSize
  1184.      */
  1185.     public synchronized Dimension getMinimumSize()
  1186.     {
  1187.         return new Dimension(20, 40);
  1188.     }
  1189.  
  1190.     /**
  1191.      * @deprecated
  1192.      * @see #getMinimumSize
  1193.      */
  1194.     public synchronized Dimension minimumSize()
  1195.     {
  1196.         return getMinimumSize();
  1197.     }
  1198.  
  1199.     /**
  1200.      * Takes no action.
  1201.      * This is a standard Java AWT method which gets called to specify
  1202.      * which layout manager should be used to layout the components in
  1203.      * standard containers.
  1204.      *
  1205.      * Since layout managers CANNOT BE USED with this container the standard
  1206.      * setLayout has been OVERRIDDEN for this container and does nothing.
  1207.      *
  1208.      * @param lm the layout manager to use to layout this container's components
  1209.      * (IGNORED)
  1210.      * @see java.awt.Container#getLayout
  1211.      **/
  1212.     public void setLayout(LayoutManager lm)
  1213.     {
  1214.     }
  1215.  
  1216.     public boolean isFocusTraversable()
  1217.     {
  1218.         return true;
  1219.     }
  1220.  
  1221.     /**
  1222.      * Tells this component that it has been added to a container.
  1223.      * This is a standard Java AWT method which gets called by the AWT when
  1224.      * this component is added to a container. Typically, it is used to
  1225.      * create this component's peer.
  1226.      *
  1227.      * It has been overridden here to hook-up event listeners.
  1228.      *
  1229.      * @see #removeNotify
  1230.      */
  1231.     public synchronized void addNotify()
  1232.     {
  1233.         super.addNotify();
  1234.         errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  1235.  
  1236.         //Hook up listeners
  1237.         if (mouse == null)
  1238.         {
  1239.             mouse = new Mouse();
  1240.             addMouseListener(mouse);
  1241.         }
  1242.         if (key == null)
  1243.         {
  1244.             key = new Key();
  1245.             addKeyListener(key);
  1246.         }
  1247.         if (adjustment == null)
  1248.         {
  1249.             adjustment = new Adjustment();
  1250.             verticalScrollBar.addAdjustmentListener(adjustment);
  1251.             horizontalScrollBar.addAdjustmentListener(adjustment);
  1252.         }
  1253.         if (focus == null)
  1254.         {
  1255.             focus = new Focus();
  1256.             addFocusListener(focus);
  1257.         }
  1258.  
  1259.     }
  1260.  
  1261.     /**
  1262.      * Tells this component that it is being removed from a container.
  1263.      * This is a standard Java AWT method which gets called by the AWT when
  1264.      * this component is removed from a container. Typically, it is used to
  1265.      * destroy the peers of this component and all its subcomponents.
  1266.      *
  1267.      * It has been overridden here to unhook event listeners.
  1268.      *
  1269.      * @see #addNotify
  1270.      */
  1271.     public synchronized void removeNotify()
  1272.     {
  1273.         //Unhook listeners
  1274.         if (mouse != null)
  1275.         {
  1276.             removeMouseListener(mouse);
  1277.             mouse = null;
  1278.         }
  1279.         if (key != null)
  1280.         {
  1281.             removeKeyListener(key);
  1282.             key = null;
  1283.         }
  1284.         if (adjustment != null)
  1285.         {
  1286.             verticalScrollBar.removeAdjustmentListener(adjustment);
  1287.             horizontalScrollBar.removeAdjustmentListener(adjustment);
  1288.             adjustment = null;
  1289.         }
  1290.         if (focus != null)
  1291.         {
  1292.             removeFocusListener(focus);
  1293.             focus = null;
  1294.         }
  1295.         super.removeNotify();
  1296.     }
  1297.  
  1298.     /**
  1299.      * Triggers redrawing the entire image, even if the size of the component
  1300.      * has not changed.
  1301.      */
  1302.     public void triggerRedraw()
  1303.     {
  1304.         redrawTriggered = true;
  1305.         repaint();
  1306.     }
  1307.  
  1308.     // -----------------------------------------
  1309.     // --------- event related methods ---------
  1310.     // -----------------------------------------
  1311.  
  1312.     /**
  1313.      * Adds the specified action listener to receive action events
  1314.      * from this button.
  1315.      * @param l the action listener
  1316.      */
  1317.     public synchronized void addActionListener(ActionListener l)
  1318.     {
  1319.         actionListener = AWTEventMulticaster.add(actionListener, l);
  1320.     }
  1321.  
  1322.     /**
  1323.      * Removes the specified action listener so it no longer receives
  1324.      * action events from this button.
  1325.      * @param l the action listener
  1326.      */
  1327.     public synchronized void removeActionListener(ActionListener l)
  1328.     {
  1329.         actionListener = AWTEventMulticaster.remove(actionListener, l);
  1330.     }
  1331.  
  1332.     /**
  1333.      * Add a listener to recieve item events when the state of
  1334.      * an item changes.
  1335.      * <p>
  1336.      * This is a standard method of the ItemSelectable interface.
  1337.      * @param l the listener to recieve events
  1338.      * @see ItemEvent
  1339.      */
  1340.     public synchronized void addItemListener(ItemListener l)
  1341.     {
  1342.         itemListener = AWTEventMulticaster.add(itemListener, l);
  1343.     }
  1344.  
  1345.     /**
  1346.      * Removes an item listener.
  1347.      * <p>
  1348.      * This is a standard method of the ItemSelectable interface.
  1349.      * @param l the listener being removed
  1350.      * @see ItemEvent
  1351.      */
  1352.     public synchronized void removeItemListener(ItemListener l)
  1353.     {
  1354.         itemListener = AWTEventMulticaster.remove(itemListener, l);
  1355.     }
  1356.  
  1357.     class Adjustment implements AdjustmentListener, java.io.Serializable
  1358.     {
  1359.         public void adjustmentValueChanged(AdjustmentEvent event)
  1360.         {
  1361.             handleAdjustmentEvent(event);
  1362.         }
  1363.     }
  1364.  
  1365.     class Mouse extends MouseAdapter implements java.io.Serializable
  1366.     {
  1367.         /**
  1368.          * Processes MOUSE_DOWN events.
  1369.          * This is a standard Java AWT method which gets called by the AWT
  1370.          * method handleEvent() in response to receiving a MOUSE_DOWN
  1371.          * event. These events occur when the mouse button is pressed while
  1372.          * inside this component.
  1373.          *
  1374.          * @param event the event
  1375.          * @param x the component-relative horizontal coordinate of the mouse
  1376.          * @param y the component-relative vertical coordinate of the mouse
  1377.          *
  1378.          * @return true if the event was handled
  1379.          *
  1380.          * @see java.awt.Component#mouseUp
  1381.          */
  1382.         public void mousePressed(MouseEvent event)
  1383.         {
  1384.             handleMousePressed(event);
  1385.         }
  1386.  
  1387.         public void mouseReleased(MouseEvent event)
  1388.         {
  1389.             handleMouseReleased(event);
  1390.         }
  1391.     }
  1392.  
  1393.     class Key extends KeyAdapter implements java.io.Serializable
  1394.     {
  1395.         /**
  1396.          * Processes KEY_PRESS and KEY_ACTION events.
  1397.          * This is a standard Java AWT method which gets called by the AWT
  1398.          * method handleEvent() in response to receiving a KEY_PRESS or
  1399.          * KEY_ACTION event. These events occur when this component has the focus
  1400.          * and the user presses a "normal" or an "action" (F1, page up, etc) key.
  1401.          *
  1402.          * @param event the Event
  1403.          * @param key the key that was pressed
  1404.          * @return true if the event was handled
  1405.          * @see java.awt.Component#keyUp
  1406.          * @see #handleEvent
  1407.          */
  1408.         public void keyPressed(KeyEvent event)
  1409.         {
  1410.             handleKeyPressed(event);
  1411.         }
  1412.     }
  1413.  
  1414.     class Focus extends FocusAdapter implements java.io.Serializable
  1415.     {
  1416.         public void focusGained(FocusEvent event)
  1417.         {
  1418.             handleFocusGained(event);
  1419.         }
  1420.  
  1421.         public void focusLost(FocusEvent event)
  1422.         {
  1423.             handleFocusLost(event);
  1424.         }
  1425.     }
  1426.  
  1427.     /**
  1428.      * Handles mouse pressed events.
  1429.      * This function will get called when the component recives a mouse pressed event.
  1430.      * Override to change the way mouse pressed is handled.
  1431.      * @param event the MouseEvent
  1432.      * @see #handleMouseReleased
  1433.      */
  1434.     protected void handleMousePressed(MouseEvent event)
  1435.     {
  1436.         requestFocus();
  1437.  
  1438.         int x = event.getX();
  1439.         int y = event.getY();
  1440.  
  1441.         int index = (y / cellSize) + sbVPosition;
  1442.  
  1443.         //If clicked below the last node
  1444.         if (index > viewCount-1)
  1445.             return;
  1446.  
  1447.         TreeNode oldNode = selectedNode;
  1448.  
  1449.         TreeNode newNode = (TreeNode)v.elementAt(index);
  1450.  
  1451.         int newDepth = newNode.getDepth();
  1452.  
  1453.         // check for toggle box click
  1454.         // todo: make it a bit bigger
  1455.         Rectangle toggleBox = new Rectangle(cellSize*newDepth + cellSize/4,
  1456.                                             (index-sbVPosition)*cellSize + clickSize/2,
  1457.                                             clickSize, clickSize);
  1458.  
  1459.         if (toggleBox.inside(x,y))
  1460.         {
  1461.             newNode.toggle();
  1462.             resetVector();
  1463.             if(!newNode.isExpanded())
  1464.             {
  1465.                 if (!v.contains(selectedNode))
  1466.                     setSelectedNode(newNode);
  1467.             }
  1468.             triggerRedraw();
  1469.             invalidate();
  1470.             sendActionEvent();
  1471.  
  1472.             if(itemListener != null)
  1473.             {
  1474.                 itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, newNode, newNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1475.             }
  1476.         }
  1477.         else
  1478.         {
  1479.             setSelectedNode(newNode);
  1480.  
  1481.             // check for double click
  1482.             long currentTime = event.getWhen();
  1483.  
  1484.             if ((newNode==oldNode) && ((event.getWhen() - timeMouseDown)<doubleClickResolution))
  1485.             {
  1486.                 newNode.toggle();
  1487.                 resetVector();
  1488.                 triggerRedraw();
  1489.                 invalidate();
  1490.                 sendActionEvent();
  1491.  
  1492.                 if(itemListener != null)
  1493.                 {
  1494.                     itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, newNode, newNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1495.                 }
  1496.  
  1497.                 return;
  1498.             }
  1499.             else
  1500.             {
  1501.                 //single click action could be added here
  1502.                 timeMouseDown = event.getWhen();
  1503.             }
  1504.  
  1505.         }
  1506.     }
  1507.  
  1508.     /**
  1509.      * Handles mouse released events.
  1510.      * This function will get called when the component recives a mouse released event.
  1511.      * Override to change the way mouse released is handled.
  1512.      * @param event the MouseEvent
  1513.      * @see #handleMousePressed
  1514.      */
  1515.     protected void handleMouseReleased(MouseEvent event)
  1516.     {
  1517.     }
  1518.  
  1519.     /**
  1520.      * Handles adjustment events.
  1521.      * This function will get called when the component recives a adjustment event.
  1522.      * Override to change the way adjustment is handled.
  1523.      * @param event the AdjustmentEvent
  1524.      */
  1525.     protected void handleAdjustmentEvent(AdjustmentEvent event)
  1526.     {
  1527.         if (event.getAdjustable() == verticalScrollBar)
  1528.         {
  1529.             if (sbVPosition != verticalScrollBar.getValue())
  1530.             {
  1531.                 sbVPosition = verticalScrollBar.getValue();
  1532.                 triggerRedraw();
  1533.             }
  1534.         }
  1535.         else
  1536.         if (event.getAdjustable() == horizontalScrollBar)
  1537.         {
  1538.             if (sbHPosition != horizontalScrollBar.getValue())
  1539.             {
  1540.                 sbHPosition = horizontalScrollBar.getValue();
  1541.                 //repaint();
  1542.                 triggerRedraw();
  1543.             }
  1544.         }
  1545.     }
  1546.  
  1547.     /**
  1548.      * Handles key pressed events.
  1549.      * This function will get called when the component recives a key pressed event.
  1550.      * Override to change the way key pressed is handled.
  1551.      * @param event the KeyEvent
  1552.      */
  1553.     protected void handleKeyPressed(KeyEvent event)
  1554.     {
  1555.         int index = v.indexOf(selectedNode);
  1556.  
  1557.         switch (event.getKeyCode())
  1558.         {
  1559.             case KeyEvent.VK_ENTER:    //enter key
  1560.             case 13:
  1561.                 sendActionEvent();
  1562.                 requestFocus();
  1563.                 break;
  1564.             case KeyEvent.VK_LEFT:    //left arrow
  1565.                 if(event.isControlDown())
  1566.                 {
  1567.                     if(sbHPosition > 0)
  1568.                     {
  1569.                         horizontalScrollBar.setValue(Math.max(sbHPosition-=sbHLineIncrement,0));
  1570.                         repaint();
  1571.                     }
  1572.                     break;
  1573.                 }
  1574.                 else
  1575.                 if (selectedNode.isExpanded())
  1576.                 {
  1577.                     selectedNode.toggle();
  1578.  
  1579.                     if(itemListener != null)
  1580.                     {
  1581.                         itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, selectedNode, selectedNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1582.                         triggerRedraw();
  1583.                     }
  1584.  
  1585.                     break;
  1586.                 }
  1587.  
  1588.                 // else drop through to "UP" with no "break;"
  1589.             case KeyEvent.VK_UP:
  1590.                 if (index > 0)
  1591.                 {
  1592.                     index--;
  1593.                     setSelectedNode((TreeNode)v.elementAt(index));
  1594.                     requestFocus();
  1595.                 }
  1596.                 break;
  1597.             case KeyEvent.VK_RIGHT:
  1598.                 if(event.isControlDown())
  1599.                 {
  1600.                     int max = horizontalScrollBar.getMaximum()-(isSun1_1?size().width-sbVWidth:0);
  1601.                     if(sbHShow && sbHPosition < max)
  1602.                     {
  1603.                         horizontalScrollBar.setValue(Math.min(sbHPosition+=sbHLineIncrement, max));
  1604.                         repaint();
  1605.                     }
  1606.                     break;
  1607.                 }
  1608.                 else
  1609.                 if (selectedNode.isExpandable() && (!selectedNode.isExpanded()))
  1610.                 {
  1611.                     selectedNode.toggle();
  1612.                     sendActionEvent();
  1613.  
  1614.                     if(itemListener != null)
  1615.                     {
  1616.                         itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, selectedNode, selectedNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1617.                         triggerRedraw();
  1618.                     }
  1619.  
  1620.                     break;
  1621.                 }
  1622.  
  1623.                 if (!selectedNode.isExpandable())
  1624.                 {
  1625.                     break;
  1626.                 }
  1627.                 // else drop thru' to DOWN
  1628.             case KeyEvent.VK_DOWN:
  1629.                 if (index < viewCount-1)
  1630.                 {
  1631.                     index++;
  1632.                     setSelectedNode((TreeNode)v.elementAt(index));
  1633.                     requestFocus();
  1634.                 }
  1635.                 break;
  1636.         }
  1637.     }
  1638.  
  1639.     /**
  1640.      * Handles focus gained events.
  1641.      * This function will get called when the component recives a focus gained event.
  1642.      * Override to change the way focus gained is handled.
  1643.      * @param event the FocusEvent
  1644.      * @see #handleFocusLost
  1645.      */
  1646.     protected void handleFocusGained(FocusEvent event)
  1647.     {
  1648.         hasFocus = true;
  1649.         if (selectedNode != null && v != null)
  1650.             drawNodeText(selectedNode, (v.indexOf(selectedNode) - sbVPosition)*cellSize, true, false);
  1651.     }
  1652.  
  1653.     /**
  1654.      * Handles focus lost events.
  1655.      * This function will get called when the component recives a focus lost event.
  1656.      * Override to change the way focus lost is handled.
  1657.      * @param event the FocusEvent
  1658.      * @see #handleFocusGained
  1659.      */
  1660.     protected void handleFocusLost(FocusEvent event)
  1661.     {
  1662.         hasFocus = false;
  1663.         if (selectedNode != null && v != null)
  1664.             drawNodeText(selectedNode, (v.indexOf(selectedNode) - sbVPosition)*cellSize, true, false);
  1665.     }
  1666.  
  1667.     /**
  1668.      * Internal helper method.
  1669.      * Draws the text of the given tree node.
  1670.      * @param node the tree node whose text is drawn
  1671.      * @param yPosition the vertical drawing position for the node
  1672.      * @param eraseBackground <code>true</code> to erase the background behind the text
  1673.      * @param eraseLines <code>true</code> to erase the node's text lines before redrawing
  1674.      */
  1675.     protected void drawNodeText(TreeNode node, int yPosition, boolean eraseBackground, boolean eraseLines)
  1676.     {
  1677.         if (node == null)
  1678.             return;
  1679.  
  1680.         Color fg, bg;
  1681.         int depth=node.depth;
  1682.         Image nodeImage = node.getImage();
  1683.         int textOffset = ((depth + 1) * (cellSize)) + cellSize + textInset - (nodeImage==null ? 12:0);
  1684.  
  1685.         if (node == selectedNode && hasFocus)
  1686.         {
  1687.             //??RKM?? Temp until these return some good values
  1688.             if (symantec.itools.lang.OS.isMacintosh())
  1689.                 fg = Color.white;
  1690.             else
  1691.                 fg = SystemColor.textHighlightText;
  1692.  
  1693.             if (symantec.itools.lang.OS.isMacintosh())
  1694.                 bg = new Color(0,0,128);
  1695.             else
  1696.                 bg = SystemColor.textHighlight;
  1697.         }
  1698.         else
  1699.         {
  1700.             fg = getForeground();
  1701.             bg = getBackground();
  1702.         }
  1703.  
  1704.         if (eraseBackground)
  1705.         {
  1706.             g1.setColor(bg);
  1707.             g1.fillRect(textOffset-1, yPosition+1, fm.stringWidth(node.text)+4, cellSize-1);
  1708.         }
  1709.  
  1710.         if (node == selectedNode)
  1711.         {
  1712.             g1.setColor(getForeground());
  1713.             g1.drawRect(textOffset-1, yPosition+1, fm.stringWidth(node.text)+3, cellSize-2);
  1714.             repaint(Math.max(0,textOffset-1-sbHPosition), yPosition+1, fm.stringWidth(node.text)+4, cellSize-1);
  1715.         }
  1716.  
  1717.         if (eraseLines)
  1718.         {
  1719.             g1.setColor(getBackground());
  1720.             g1.drawRect(textOffset-1, yPosition+1, fm.stringWidth(node.text)+3, cellSize-2);
  1721.             repaint(Math.max(0,textOffset-1-sbHPosition), yPosition+1, fm.stringWidth(node.text)+4, cellSize-1);
  1722.         }
  1723.  
  1724.         g1.setColor(fg);
  1725.         g1.drawString(node.text, textOffset, yPosition + cellSize - textBaseLine);
  1726.     }
  1727.  
  1728.     /**
  1729.      * Sends an action performed event to any action listeners, as needed.
  1730.      */
  1731.     protected void sendActionEvent()
  1732.     {
  1733.         if (actionListener != null)
  1734.             actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, new String(selectedNode.getText())));
  1735.     }
  1736.  
  1737.     /**
  1738.      * Calculates the maximum width of the tree.
  1739.      * @param gg the Graphics context to use for the calculation
  1740.      * @return the maximum tree width, in pixels
  1741.      */
  1742.     protected int compWidth(Graphics gg)
  1743.     {
  1744.         int size = 0;
  1745.         int textOffset;
  1746.         TreeNode node;
  1747.  
  1748.            Font f = getFont();  //unix version might not provide a default font
  1749.         //Make certain there is a font
  1750.         if (f == null)
  1751.         {
  1752.             f = new Font("Serif", Font.PLAIN, 13);
  1753.             if(gg != null)
  1754.                    gg.setFont(f);
  1755.             setFont(f);
  1756.         }
  1757.  
  1758.         if(gg == null)
  1759.             fm = null;
  1760.         else
  1761.             fm = gg.getFontMetrics();
  1762.  
  1763.         if(fm == null)
  1764.             fm = getFontMetrics(f);
  1765.  
  1766.         if(fm == null || v == null)
  1767.             size = 100;
  1768.         else
  1769.         {
  1770.             for (int i=0; i < v.size(); i++)
  1771.             {
  1772.                 node = (TreeNode)v.elementAt(i);
  1773.                    textOffset = ((node.depth + 1) * (cellSize)) + cellSize + textInset - (node.getImage()==null ? 12:0);
  1774.                    if (size < (textOffset+fm.stringWidth(node.text)+6))
  1775.                     size = textOffset+fm.stringWidth(node.text)+6;
  1776.             }
  1777.         }
  1778.  
  1779.         return size;
  1780.     }
  1781.  
  1782.     /**
  1783.      * Internal helper method.
  1784.      * Draws a dotted line.
  1785.      * @param x0 starting x position
  1786.      * @param y0 starting y position
  1787.      * @param x1 ending x position
  1788.      * @param y1 ending y position
  1789.      */
  1790.     protected void drawDotLine(int x0, int y0, int x1, int y1)
  1791.     {
  1792.        if (y0==y1)
  1793.        {
  1794.             for (int i = x0; i<x1; i+=2)
  1795.             {
  1796.                g1.drawLine(i,y0, i, y1);
  1797.             }
  1798.         }
  1799.         else
  1800.         {
  1801.             for (int i = y0; i<y1; i+=2)
  1802.             {
  1803.                 g1.drawLine(x0, i, x1, i);
  1804.             }
  1805.         }
  1806.     }
  1807.  
  1808.     /**
  1809.      * Handles selecting the given node.
  1810.      * @param node the node to select
  1811.      */
  1812.     protected void changeSelection(TreeNode node)
  1813.     {
  1814.         setSelectedNode(node);
  1815.     }
  1816.  
  1817.     /**
  1818.      * Handles selecting the given node.
  1819.      * @param node the node to select
  1820.      */
  1821.     public void setSelectedNode(TreeNode node)
  1822.     {
  1823.         if(node == null)
  1824.         {
  1825.             if(selectedNode != null)
  1826.             {
  1827.                 drawNodeText(selectedNode, (v.indexOf(selectedNode) - sbVPosition) * cellSize, true, true);
  1828.  
  1829.                 if(itemListener != null)
  1830.                 {
  1831.                     itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectedNode, ItemEvent.DESELECTED));
  1832.                 }
  1833.  
  1834.                 selectedNode = null;
  1835.             }
  1836.  
  1837.             return;
  1838.         }
  1839.  
  1840.         if (node == selectedNode)
  1841.             return;
  1842.  
  1843.         TreeNode oldNode = selectedNode;
  1844.         selectedNode = node;
  1845.  
  1846.         if(oldNode != null)
  1847.         {
  1848.             drawNodeText(oldNode, (v.indexOf(oldNode) - sbVPosition)*cellSize, true, false);
  1849.         }
  1850.  
  1851.         drawNodeText(node, (v.indexOf(node) - sbVPosition)*cellSize, true, false);
  1852.  
  1853.         // send select event
  1854.  
  1855.         int index = v.indexOf(selectedNode);
  1856.  
  1857.         if (itemListener != null)
  1858.         {
  1859.             if(oldNode != null)
  1860.             {
  1861.                 itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, oldNode, ItemEvent.DESELECTED));
  1862.             }
  1863.  
  1864.             itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectedNode, ItemEvent.SELECTED));
  1865.         }
  1866.  
  1867.         if (index < sbVPosition)
  1868.         { //scroll up
  1869.             sbVPosition--;
  1870.             verticalScrollBar.setValue(sbVPosition);
  1871.             triggerRedraw();
  1872.             return;
  1873.         }
  1874.  
  1875.         if (index >= sbVPosition + (viewHeight-cellSize/2)/cellSize)
  1876.         {
  1877.             sbVPosition++;
  1878.             verticalScrollBar.setValue(sbVPosition);
  1879.             triggerRedraw();
  1880.             return;
  1881.         }
  1882.  
  1883.         repaint();
  1884.     }
  1885.  
  1886.     /**
  1887.      * Generates a new tree node.
  1888.      * Override if you want to return your own instantiation of a TreeNode subclas.
  1889.      * @param text the node text
  1890.      * @param treeView a reference to the parent TreeView (usually "this").
  1891.      * @return a new TreeNode
  1892.      */
  1893.     protected TreeNode newTreeNode(String text, TreeView treeView)
  1894.     {
  1895.         return new TreeNode(text, treeView);
  1896.     }
  1897.  
  1898.     /**
  1899.      * Internal helper method.
  1900.      * Process the given array of Strings, creating TreeNodes and appending them to this
  1901.      * component. Existing TreeNodes are kept.
  1902.      * @param tempStructure the String array to parse
  1903.      * @exception InvalidTreeNodeException if there is a problem with the String array values
  1904.      */
  1905.     protected void parseTreeStructure(String tempStructure[]) throws InvalidTreeNodeException
  1906.     {
  1907.         for(int i = 0; i < tempStructure.length; i++)
  1908.         {
  1909.             String entry = tempStructure[i];
  1910.             int indentLevel = findLastPreSpace(entry)/*+1*/;
  1911.  
  1912.             if (indentLevel == -1)
  1913.                 throw new InvalidTreeNodeException("parseTreeStructure: " + errors.getString("EmptyStrings"));
  1914.  
  1915.             TreeNode node = newTreeNode(entry.trim(), this);
  1916.             node.setDepth(indentLevel);
  1917.  
  1918.             if (rootNode == null)
  1919.             {
  1920.                 if (indentLevel != 0)
  1921.                     throw new InvalidTreeNodeException("parseTreeStructure: " + errors.getString("NoRootLevelNode"));
  1922.  
  1923.                 append(node);
  1924.             }
  1925.             else
  1926.             {
  1927.                 TreeNode currentNode = rootNode;
  1928.                 while(currentNode.sibling != null)
  1929.                     currentNode = currentNode.sibling;
  1930.  
  1931.                 for(int j = 1; j < indentLevel; j++)
  1932.                 {
  1933.                     int numberOfChildren = currentNode.numberOfChildren;
  1934.                     TreeNode tempNode = null;
  1935.  
  1936.                     if (numberOfChildren > 0)
  1937.                     {
  1938.                         tempNode = currentNode.child;
  1939.  
  1940.                         while(tempNode.sibling != null)
  1941.                             tempNode = tempNode.sibling;
  1942.                     }
  1943.  
  1944.                     if (tempNode != null)
  1945.                         currentNode = tempNode;
  1946.                     else
  1947.                         break;
  1948.                 }
  1949.  
  1950.                 int diff = indentLevel - currentNode.getDepth();
  1951.  
  1952.                 if (diff > 1)
  1953.                     throw new InvalidTreeNodeException("parseTreeStructure: " + errors.getString("NoParent") + entry.trim());
  1954.  
  1955.                 if (diff == 1)
  1956.                     insert(node, currentNode, CHILD);
  1957.                 else
  1958.                     insert(node, currentNode, NEXT);
  1959.             }
  1960.         }
  1961.     }
  1962.  
  1963.     /**
  1964.      * Internal helper method.
  1965.      * Rebuilds the Vector e of nodes, adjusting node depth and maintaining an
  1966.      * overall count as it goes along.
  1967.      * Calls the recursive method traverse to do the meat of the job.
  1968.      */
  1969.     protected void recount()
  1970.     {
  1971.         count = 0;
  1972.         e = new Vector();
  1973.  
  1974.         if (rootNode != null)
  1975.         {
  1976.             rootNode.depth=0;
  1977.             traverse(rootNode);
  1978.         }
  1979.     }
  1980.  
  1981.     /**
  1982.      * Internal helper method.
  1983.      * Recursive method that rebuilds the Vector e of nodes,
  1984.      * adjusting node depth and maintaining an
  1985.      * overall count as it goes along.
  1986.      */
  1987.     protected void traverse(TreeNode node)
  1988.     {
  1989.         count++;
  1990.  
  1991.         if(!node.isHidden())
  1992.         {
  1993.             e.addElement(node);
  1994.  
  1995.             if (node.child != null)
  1996.             {
  1997.                 node.child.depth = node.depth+1;
  1998.                 traverse(node.child);
  1999.             }
  2000.         }
  2001.  
  2002.         if (node.sibling != null)
  2003.         {
  2004.             node.sibling.depth = node.depth;
  2005.             traverse(node.sibling);
  2006.         }
  2007.     }
  2008.  
  2009.     /**
  2010.      * Internal helper method.
  2011.      * Traverses tree to put nodes into vector v
  2012.      * for internal processing. Depths of nodes are set,
  2013.      * and viewCount and viewWidest is set.
  2014.      * Calls the recursive method vectorize to do the meat of the job.
  2015.      */
  2016.     protected void resetVector()
  2017.     {
  2018.         // Traverses tree to put nodes into vector v
  2019.         // for internal processing. Depths of nodes are set,
  2020.         // and viewCount and viewWidest is set.
  2021.         v = new Vector(count);
  2022.         viewWidest = 30;
  2023.  
  2024.         if (count < 1)
  2025.         {
  2026.             viewCount = 0;
  2027.             return;
  2028.         }
  2029.  
  2030.         rootNode.depth = 0;
  2031.         vectorize(rootNode, true, true, v);
  2032.         viewCount = v.size();
  2033.  
  2034.         needResetVector = false;
  2035.     }
  2036.  
  2037.     /**
  2038.      * Internal helper method.
  2039.      * Traverses tree to put nodes into the given vector for for processing.
  2040.      * Depths of nodes are set.
  2041.      * @param node the current node
  2042.      * @param respectExpanded <code>true</code> to only follow expanded child links
  2043.      * @param repectHidden <code>true</code> to not follow hidden child links
  2044.      * @param nodeVector Vector to add the processed nodes to
  2045.      */
  2046.     protected void vectorize(TreeNode node, boolean respectExpanded, boolean repectHidden, Vector nodeVector)
  2047.     {
  2048.         if(node == null)
  2049.         {
  2050.             return;
  2051.         }
  2052.  
  2053.         if(!repectHidden || !node.isHidden())
  2054.         {
  2055.             nodeVector.addElement(node);
  2056.  
  2057.             if((!respectExpanded && node.child != null) || node.isExpanded())
  2058.             {
  2059.                 node.child.depth = node.depth + 1;
  2060.                 vectorize(node.child, respectExpanded, repectHidden, nodeVector);
  2061.             }
  2062.         }
  2063.  
  2064.         if (node.sibling != null)
  2065.         {
  2066.             node.sibling.depth = node.depth;
  2067.             vectorize(node.sibling, respectExpanded, repectHidden, nodeVector);
  2068.         }
  2069.     }
  2070.  
  2071.     /**
  2072.      * Internal use only.
  2073.      */
  2074.     protected void debugVector()
  2075.     {
  2076.         int vSize = v.size();
  2077.  
  2078.         for (int i=0; i<count; i++)
  2079.         {
  2080.             TreeNode node = (TreeNode) v.elementAt(i);
  2081.             System.out.println(node.text);
  2082.         }
  2083.     }
  2084.  
  2085.     /**
  2086.      * Internal helper method.
  2087.      * Counts the number of spaces before the node text starts.
  2088.      * @param s the node text
  2089.      * @return the number of spaces before the texxt starts
  2090.      */
  2091.     protected int findLastPreSpace(String s)
  2092.     {
  2093.         if(s != null && s.length() > 0)
  2094.         {
  2095.             int length;
  2096.  
  2097.             length = s.length();
  2098.  
  2099.             if(s.charAt(0) != ' ' && s.charAt(0) != '\t')
  2100.             {
  2101.                 return 0;
  2102.             }
  2103.  
  2104.             for(int i = 1; i < length; i++)
  2105.             {
  2106.                 if(s.charAt(i) != ' ' && s.charAt(i) != '\t')
  2107.                 {
  2108.                     return i;
  2109.                 }
  2110.             }
  2111.         }
  2112.         return -1;
  2113.     }
  2114.  
  2115.  
  2116.     int        sbVPosition                = 0;    // hold value of vertical scrollbar
  2117.     int        sbVWidth;                           // width of vertical scrollbar
  2118.     int        sbHPosition                = 0;    // hold value of horizontal scrollbar
  2119.     int        sbHHeight                = 0;    // height of horizontal scrollbar
  2120.     long    sbVTimer                = -1;    // time of last vert scrollbar event
  2121.     int        cellSize                   = 16;   // size of node image
  2122.     int        clickSize               = 8;    // size of mouse toggle (plus or minus)
  2123.     int        imageInset                = 3;    // left margin of node image
  2124.     int        textInset               = 6;    // left margin for text
  2125.     int        textBaseLine            = 3;    // position of font baseline from bottom of cell
  2126.     int        doubleClickResolution    = 333;    // double-click speed in milliseconds
  2127.  
  2128.     /**
  2129.      * root node of tree
  2130.      */
  2131.     protected TreeNode rootNode;
  2132.     /**
  2133.      * highlighted node
  2134.      */
  2135.     protected TreeNode selectedNode;
  2136.     /**
  2137.      * first node in window
  2138.      */
  2139.     protected TreeNode topVisibleNode;
  2140.     /**
  2141.      * The vertical scrollbar.
  2142.      */
  2143.     protected Scrollbar    verticalScrollBar;
  2144.     /**
  2145.      * show or hide vertical scrollbar
  2146.      */
  2147.     protected boolean sbVShow = false;
  2148.     /**
  2149.      * Number of nodes in the tree.
  2150.      */
  2151.     protected int count = 0;
  2152.     /**
  2153.      * Number of viewable nodes in the tree.
  2154.      * A node is viewable if all of its parents are expanded.
  2155.      */
  2156.     protected int    viewCount = 0;
  2157.     /**
  2158.      * The horizontal scrollbar
  2159.      */
  2160.     protected Scrollbar horizontalScrollBar;
  2161.     /**
  2162.      * size of horizontal scrollbar
  2163.      */
  2164.     protected int sbHSize;
  2165.     /**
  2166.      * for horizontal scrollbar
  2167.      */
  2168.     protected int newWidth = 0;
  2169.     /**
  2170.      * show or hide horizontal scrollbar
  2171.      */
  2172.        protected boolean sbHShow = false;
  2173.        /**
  2174.         * Keeps track of if drawTree needs to call resetVector or not.
  2175.         */
  2176.        protected boolean needResetVector;
  2177.  
  2178.     /**
  2179.      * Horizontal scroll bar unit increment.
  2180.      */
  2181.     protected int sbHLineIncrement = 4;
  2182.        /**
  2183.         * pixel size of tree display
  2184.         */
  2185.     protected int viewHeight = 300;
  2186.        /**
  2187.         * pixel size of tree display
  2188.         */
  2189.     protected int viewWidth  = 300;
  2190.        /**
  2191.         * widest item displayable (for horz scroll)
  2192.         */
  2193.     protected int viewWidest = 0 ;
  2194.     /**
  2195.      * The this component's key event listener.
  2196.      */
  2197.     protected Key key = null;
  2198.     /**
  2199.      * The this component's mouse event listener.
  2200.      */
  2201.     protected Mouse mouse = null;
  2202.     /**
  2203.      * The this component's adjustment event listener.
  2204.      */
  2205.     protected Adjustment adjustment = null;
  2206.     /**
  2207.      * The action listener to keep track of listeners for our action event.
  2208.      */
  2209.     protected ActionListener actionListener = null;
  2210.     /**
  2211.      * The action listener to keep track of listeners for our item event.
  2212.      */
  2213.     protected ItemListener itemListener = null;
  2214.     /**
  2215.      * The this component's focus event listener.
  2216.      */
  2217.     protected Focus focus = null;
  2218.     /**
  2219.      * v is vector of viewable nodes
  2220.      */
  2221.     protected Vector v;
  2222.     /**
  2223.      * e is vector of existing nodes
  2224.      */
  2225.     protected Vector e;
  2226.     /**
  2227.      * Flag indicating repaint() shoud redraw the entire image even
  2228.      * if the component size has not changed.
  2229.      */
  2230.     transient protected boolean redrawTriggered = false;
  2231.     transient boolean hasFocus = false;
  2232.        /**
  2233.         * current font metrics
  2234.         */
  2235.     transient protected FontMetrics fm;
  2236.        /**
  2237.         * save time of last mouse down (for double click)
  2238.         */
  2239.     transient long timeMouseDown;
  2240.     /**
  2241.      * Offscreen Image used for buffering the painting process.
  2242.      */
  2243.     transient protected Image im1;
  2244.     /**
  2245.      * Offscreen graphics context used for buffering the painting process.
  2246.      */
  2247.     transient protected Graphics g1 = null;
  2248.     /**
  2249.      * Checks for scrollbars that have different max value.
  2250.      */
  2251.     static protected boolean isSun1_1;
  2252.  
  2253.     /**
  2254.      * Error strings.
  2255.      */
  2256.     transient protected ResourceBundle errors;
  2257.  
  2258.     static
  2259.     {
  2260.         //Calc it once
  2261.         String vendor = System.getProperty("java.vendor");
  2262.         String version = System.getProperty("java.version");
  2263.  
  2264.         isSun1_1 = ((vendor.startsWith("Sun Microsystems Inc.") ||
  2265.                     (vendor.startsWith("Apple"))                ||
  2266.                     (vendor.startsWith("Symantec Corporation")) ||
  2267.                     (vendor.startsWith("Netscape"))) &&
  2268.                      ((version.startsWith("11")) ||
  2269.                     (version.startsWith("1.1"))));
  2270.     }
  2271. }
  2272.  
  2273. class InvalidTreeNodeException extends Exception
  2274. {
  2275.     public InvalidTreeNodeException()
  2276.     {
  2277.         super();
  2278.     }
  2279.  
  2280.     public InvalidTreeNodeException(String message)
  2281.     {
  2282.         super(message);
  2283.     }
  2284. }
  2285.